/*
 * Parser.cpp
 *
 *  Created on: 02/06/2013
 *      Author: lucio
 */

#include "Parser.h"
#include <stdlib.h>

Parser::Parser(string root) {
	//Abro el archivo paths
	string pathArchivoPaths = ROOT;
	pathArchivoPaths += "ArchivoPaths.txt";
	this->archivoPaths.open(pathArchivoPaths.c_str(), fstream::trunc | fstream::out);

	//Chequeo que se haya podido abrir correctamente
	if (!this->archivoPaths.is_open()) {
		/* limpia los flags de control de estado del archivo */
		this->archivoPaths.clear();
		throw string("El archivo no pudo ser abierto para escritura");
	}

	//Abro el archivo indice a paths
	string pathIndicePaths = ROOT;
	pathIndicePaths += "IndicePaths.dat";
	this->indicePaths.open(pathIndicePaths.c_str(), fstream::trunc | fstream::out | fstream::binary);

	//Finalmente escribe los paths en el archivoPaths
	this->archivoPaths << root;
	this->archivoPaths << endl;

	this->raiz = root;

	//inicializo el generador de idDocumento
	this->idDocumento = 0;

	//Creo un lista de objetos para ir guardando informacion necesaria para el merge
	this->listaDeArchivoAuxMerge = new list<ArchivoAuxMerge*>();

}

void Parser::guardarPath(string path) {
	//Toma el offset en donde se va a guardar el path
	int offset = archivoPaths.tellp();

	//Escribe el offset en donde se va a guardar el path en el archivo indicePaths
	this->indicePaths.write((char*) &offset, sizeof(int));

	//Finalmente escribe los paths en el archivoPaths
	this->archivoPaths << path;
	this->archivoPaths << endl;
}

void Parser::rellenarConCeros(string &cadena){
	int cantidadARellenar = 3 - cadena.length();

	for(int i = 0; i < cantidadARellenar; i++){
		cadena = "0" + cadena;
	}
}

void Parser::parsear(string path) {
	//extrae los tokens
	list<string>* tokens = tokenizar(path);

	//ordena lexicograficamente, obtiene frecuencia y elimina duplicados
	list<RegistroTermino*>* terminos = generarTerminosYPosiciones(tokens);

	//guarda los terminos en un archivo auxiliar
	generarAuxiliar(terminos);

	//guarda el path del documento de texto procesado
	//le borra primer parte que es la raiz, que ya esta guardada
	path.erase(0,this->raiz.length());
	guardarPath(path);
}


void Parser::guardarArchivoAuxMerge(string nombreArchivoAuxiliar, int cantidadDeRegistros){
	//guardo la cantidad de registros y el path del archivo en un objeto ArchivoAuxMerge

	ArchivoAuxMerge* auxMerge = new ArchivoAuxMerge();
	char* nombreArchivo = (char*) malloc(sizeof(char) * 23);
	strcpy(nombreArchivo, nombreArchivoAuxiliar.c_str());
	auxMerge->setNombreArchivo(nombreArchivo);
	auxMerge->setCantidadElementos(cantidadDeRegistros);
	this->listaDeArchivoAuxMerge->push_back(auxMerge);
}

list<ArchivoAuxMerge*>* Parser::obtenerArchivoAuxMerge(){
	return this->listaDeArchivoAuxMerge;
}

void Parser::generarAuxiliar(list<RegistroTermino*>* terminos) {
	//Paso el idDocumento a string
	int idDoc = this->idDocumento;
	string stringIdDoc = convertirIntAString(idDoc);
	rellenarConCeros(stringIdDoc);
	string nombreArchivoAuxiliar = ROOT;
	nombreArchivoAuxiliar = "ArchivoAuxiliar" + stringIdDoc + ".txt";

	//Guarda informacion que utilizara el merge de archivos auxiliares
	guardarArchivoAuxMerge(nombreArchivoAuxiliar, terminos->size());

	fstream archivoAuxiliar;
	archivoAuxiliar.open(nombreArchivoAuxiliar.c_str(), fstream::trunc | fstream::out);

	list<RegistroTermino*>::iterator it;
	bool change = false;
	string lastTerm = "";
	int step = 0;
	for(it=terminos->begin(); it!=terminos->end(); it++){
		RegistroTermino *unRegistroTermino = *it;
		string termino = unRegistroTermino->obtenerTermino();

		//completar con la configuracion del tp
		int posicion = unRegistroTermino->obtenerPos();

		if(lastTerm != termino || step == 0)
			change = true;

		if (lastTerm != termino && step != 0){
			archivoAuxiliar << " ";
			archivoAuxiliar << idDoc;
			archivoAuxiliar << endl;
		}

		//Escribo el termino, la frecuencia y el idDoc
		if (step == 0 || change == true) {
			archivoAuxiliar << termino;
			archivoAuxiliar << " ";
			archivoAuxiliar << posicion;
			step++;
			change = false;
		} else if (change == false) {
			archivoAuxiliar << " ";
			archivoAuxiliar << posicion;
		}

		lastTerm = unRegistroTermino->obtenerTermino();

		delete unRegistroTermino;
	}

	archivoAuxiliar << " ";
	archivoAuxiliar << idDoc;
	archivoAuxiliar << endl;

	delete terminos;

	//Aumento el id de documentos
	this->idDocumento++;
	archivoAuxiliar.close();
}

string Parser::convertirIntAString(int entero){
	ostringstream flujoDeConversion;
	flujoDeConversion << entero;
	return flujoDeConversion.str();
}

void Parser::pasarAMinuscula(string &token){
	string::iterator iter;

	for( iter = token.begin(); iter != token.end(); ++iter ) {
			*iter = tolower(*iter);
	}
}

void Parser::quitarAcentosYDieresis(string &token){
	map<string,string> caracteres;
	map<string,string>::iterator it;
	string caracter;
	string caracter_reemplazo;
	size_t found;

	caracteres.insert (pair<string,string>("á","a") );
	caracteres.insert (pair<string,string>("é","e") );
	caracteres.insert (pair<string,string>("í","i") );
	caracteres.insert (pair<string,string>("ó","o") );
	caracteres.insert (pair<string,string>("ú","u") );
	//Las paso directamente a minuscula
	caracteres.insert (pair<string,string>("Á","a") );
	caracteres.insert (pair<string,string>("É","e") );
	caracteres.insert (pair<string,string>("Í","i") );
	caracteres.insert (pair<string,string>("Ó","o") );
	caracteres.insert (pair<string,string>("Ú","u") );
	caracteres.insert (pair<string,string>("ä","a") );
	caracteres.insert (pair<string,string>("ë","e") );
	caracteres.insert (pair<string,string>("ï","i") );
	caracteres.insert (pair<string,string>("ö","o") );
	caracteres.insert (pair<string,string>("ü","u") );
	caracteres.insert (pair<string,string>("Ä","a") );
	caracteres.insert (pair<string,string>("Ë","e") );
	caracteres.insert (pair<string,string>("Ï","i") );
	caracteres.insert (pair<string,string>("Ö","o") );
	caracteres.insert (pair<string,string>("Ü","u") );
	caracteres.insert (pair<string,string>("ñ","n") );

	for (it= caracteres.begin(); it != caracteres.end(); it++){
		caracter=(it->first);
		found = token.find(caracter);

		if(found != string::npos){
			caracter_reemplazo=(it->second);

			token.erase(found,caracter.length());
			token.replace(found,0,caracter_reemplazo,0,1);
		}
	}
}

void Parser::quitarSignosDePuntuacion(string &token){
	size_t pos;
	pos = token.find_first_of(".,;:()\"{}[]?¿!¡_-'—=×*$&#");

	while (pos!=string::npos){
		token.erase(pos,1);
		pos=token.find_first_of(".,;:()\"{}[]?¿!¡_-'—=×*$&#",pos);
	}
}

list<string>* Parser::tokenizar(string path){
	fstream documento;

	//abro el documento para lectura
	documento.open(path.c_str(), fstream::in);

	if (!documento.is_open()) {
		/* limpia los flags de control de estado del archivo */
		documento.clear();
		throw string("El documento no pudo ser abierto para lectura");
	}

	list<string>* tokens = new list<string>();

	string linea;
	int i = 0;
	//leo una linea
	getline(documento,linea);
	while(!documento.eof()){

		//paso la linea a un string
		string cadena (linea);

		//busco tokens en cadena delimitados por blancos o guiones
		size_t pos = cadena.find_first_of(" -|\n");

		while(pos != string::npos){
			string unToken = cadena.substr(0,pos);
			pasarAMinuscula(unToken);
			quitarAcentosYDieresis(unToken);
			quitarSignosDePuntuacion(unToken);
			if(unToken != "")//Chequeo que el token no este vacio
				tokens->push_back(unToken);
			cadena.erase(0,pos+1);
			pos = cadena.find_first_of(" -|");
		}
		i++;
		//agrego el pedazo de cadena que quedo al final que es un token
		if (cadena.length() != 0){
			//le quito el /0 del final
			cadena.erase(cadena.length()-1,1);
			pasarAMinuscula(cadena);
			quitarAcentosYDieresis(cadena);
			quitarSignosDePuntuacion(cadena);
			if(cadena != "")//Chequeo que el token no este vacio
				tokens->push_back(cadena);
		}
		//else{
			//break;
		//}

		//leo una linea
		if (!documento.eof()) {
			getline(documento,linea);
		}
	}
	documento.close();

	return tokens;
}

list<RegistroTermino*>* Parser::generarTerminosYPosiciones(list<string>* tokens){
	//tokens->sort();

	list<RegistroTermino*>* terminos = new list<RegistroTermino*>();

	list<string>::iterator it = tokens->begin();
	string unToken = *it;
	it++;
	int posicion = 0;

	while(it!=tokens->end()){
		posicion++;
		string tokenSiguiente = *it;

		RegistroTermino* unRegistroTermino = new RegistroTermino(unToken, posicion);
		//this->insertTermino(terminos,unRegistroTermino);
		terminos->push_back(unRegistroTermino);
		//terminos->sort(&RegistroTermino::ComparePredicat);
		unToken = tokenSiguiente;
		if(it!=tokens->end())
			it++;
	}
	posicion++;
	RegistroTermino* unRegistroTermino = new RegistroTermino(unToken, posicion);

	//this->insertTermino(terminos,unRegistroTermino);
	terminos->push_back(unRegistroTermino);

	terminos->sort(&RegistroTermino::ComparePredicat);
	delete tokens;
	return terminos;
}

Parser::~Parser() {
	this->indicePaths.close();
	this->archivoPaths.close();
}

